#include "timmod_test.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>


//static ismd_dev_handle_t clock_dev;
static avdl_timmod_handle_t timmod_dev;
static ismd_clock_handle_t clock_handle;
static ismd_time_t base_time = 0;
static FILE *fd = NULL;
static int pts_remain = 0;
static int channel_opened = 0;
avdl_timmod_channel_handle_t channel_handle[MAX_CHANNEL];
ismd_time_t using_time_offset = 0;
test_case_t test_case;
ismd_time_t stream_time = 0;

static void get_random_num(uint32_t k,uint32_t *rand_num)
{
	*rand_num = rand() % k;
	//printf("rand:%d\n",*rand_num);
	return;
}

static void save_test_result(uint32_t i, ismd_time_t using_time, ismd_time_t pts_in, ismd_time_t pts_out)
{
   char temp[1024];

   if(NULL == fd) {
      if (NULL == (fd = fopen(TEST_RESULT_FILE, "w+"))){
		  printf("open %s failed",TEST_RESULT_FILE);
		  return;
	  }
   }

   fprintf(fd,"chn:%d\tIn\t%lld\tOut\t%lld\tTime\t%lld\t",i ,pts_in, pts_out, using_time);
	if (i == (channel_opened - 1)){
		fprintf(fd,"\n");
	}
   //sprintf(temp, "%lld %lld %lld\n", using_time, pts_in, pts_out);
}

static void get_stream_time(ismd_time_t *using_time)
{
//#ifdef _USER_TEST_
//	get_pc_time(using_time);
//#else
//   avdl_result_t result;
//   CHECK_RESULT(ismd_clock_get_time(clock_dev, clock_handle, using_time));
//#endif
//   if (!using_time_offset){
//	   using_time_offset = *using_time - 1234;
//   }
//   *using_time -= using_time_offset;

	if (!(*using_time)){
		*using_time = 1234;
	}else{
		*using_time += 90000/30 + 1222;	
	}

}

static void gen_pts_offset(ismd_time_t *pts_offset)
{
   uint32_t r,r2;

   get_random_num((uint32_t)10000,&r);
   get_random_num((uint32_t)3,&r2);
   switch (test_case){
		case NORMAL_TEST: 
			   *pts_offset = (ismd_time_t)(PTS_OFFSET + r);
		   break;
		case DISCONTINUITY_TEST: 
		  // if ( pts_remain % 18){
			   *pts_offset = (ismd_time_t)(PTS_OFFSET* (((PTS_NUM_FOR_TEST - pts_remain)/18) + 1)*3 + r);
		   //}else{
			//   *pts_offset = (ismd_time_t)(PTS_OFFSET + r);
		   //}
		   break;
		case ODD_POINT_TEST: 
		   if (0 == pts_remain % 10){
			   *pts_offset = (ismd_time_t)((PTS_OFFSET/(r2+1))*(r2+7) + r);
		   }else{
			   *pts_offset = (ismd_time_t)(PTS_OFFSET + r);
		   }
		   break;
		default:
		   printf("Unkown test case\n");
		   break;
   }
   printf("offset:%lld\n",*pts_offset);
}

int normal_test(void)
{
   ismd_time_t pts_in;
   ismd_time_t pts_out = 0;
   ismd_time_t pts_offset = 0;
	uint32_t i,tmp;
 
   avdl_result_t result;

	gen_pts_offset(&pts_offset);
   get_stream_time(&stream_time);

	for (i = 0;i < channel_opened;i++){
		get_random_num(5000,&tmp);

		pts_in = (uint64_t )(stream_time + pts_offset + tmp + i*PTS_OFFSET/2); 

		result = avdl_timmod_user_pts_transform(timmod_dev, channel_handle[i],
																pts_in, stream_time, stream_time, &pts_out);
		assert(AVDL_SUCCESS == result);
		printf("RESULT:channel:%d\tpts_in:%lld\tpts_out:%lld\tStreamTime:%lld\n", 
				i,pts_in,pts_out,stream_time);
		save_test_result(i,stream_time, pts_in, pts_out);
	}
   return 1;
}

#ifndef _WIN32_USER_
int schedule_work(suseconds_t time_interval)
{
   int res = 0;

   struct itimerval tick;
   // Register function to SIGALRM
   signal(SIGALRM, (void *)normal_test);

   // Initialize struct
   memset(&tick, 0, sizeof(tick));
   // Timeout to run function first time
   tick.it_value.tv_sec = 0;  // sec
   tick.it_value.tv_usec = time_interval*1000; // micro sec.
   // Interval time to run function
   tick.it_interval.tv_sec = 1;
   tick.it_interval.tv_usec = 0;
   // Set timer, ITIMER_REAL : real-time to decrease timer,
   //                          send SIGALRM when timeout
   res = setitimer(ITIMER_REAL, &tick, NULL);
   assert(0==res);


   
   return 1;
}
#endif

#ifdef _WIN32_USER_
int do_once(suseconds_t time_interval)
{
	LARGE_INTEGER start_t,cur_t,interval_t;
	LARGE_INTEGER litmp; 
	
	memset(&start_t, 0, sizeof(start_t));
	memset(&cur_t, 0, sizeof(cur_t));
	memset(&interval_t, 0, sizeof(interval_t));
	memset(&litmp, 0, sizeof(litmp));

	
	QueryPerformanceFrequency(&litmp);
		interval_t.QuadPart =   (time_interval*litmp.QuadPart)/1000;
		
	QueryPerformanceCounter(&start_t);
	cur_t = start_t;
	while(cur_t.QuadPart - start_t.QuadPart < interval_t.QuadPart){
	QueryPerformanceCounter(&cur_t);	
	}

	normal_test();

   
   return 1;
}
#endif


void open_devs(void)
{
   avdl_result_t result = 1;
	uint32_t i;

#if 0
   /**************************/
//   printf("ismd_dev_open: devtype=%d...", 0);
//   CHECK_RESULT(ismd_dev_open(0, 0, NULL, &clock_dev));

   /**************************/
   printf("clock_open:...\n");
   CHECK_RESULT(ismd_clock_open(clock_dev, ISMD_CLOCK_TYPE_FIXED, &clock_handle));
#endif

   CHECK_RESULT(avdl_timmod_user_open(&timmod_dev));
	//indicate how many channels to open
	channel_opened = 2;
	for (i = 0;i < channel_opened;i++){
   CHECK_RESULT(avdl_timmod_user_open_channel(timmod_dev, 1, &channel_handle[i]));
	}

}

void init_devs(void)
{
   /**************************/
   printf("get_time:...\n");
#ifdef _USER_TEST_
   get_pc_time(&base_time);
#else
   CHECK_RESULT(ismd_clock_get_time(clock_dev, clock_handle, &base_time));
#endif
   printf("base time %lld\n",base_time);

   /*** set TimMod clock_handle**********************/
   avdl_timmod_user_set_clock(timmod_dev, clock_handle);
   avdl_timmod_user_set_play_rate(timmod_dev, 1);
   avdl_timmod_user_set_base_time(timmod_dev, base_time);
}

void close_devs(void)
{
   avdl_result_t result;

#if 0
   /**************************/
   printf("close_clock:...");
   CHECK_RESULT(ismd_clock_close(clock_dev, clock_handle));
   printf("close_dev:...");
   CHECK_RESULT(ismd_dev_close(clock_dev));
#endif

   result = avdl_timmod_user_close(timmod_dev);
   assert(AVDL_SUCCESS == result);

   fclose(fd);
}

void do_test(void)
{
   suseconds_t time_interval;
   avdl_result_t result;
   pts_remain = PTS_NUM_FOR_TEST;


   // Always sleep to catch SIGALRM signal
   while(pts_remain --) {
      //set time interval (ms)
      time_interval = 30;
#ifdef _WIN32_USER_
	normal_test();
//	do_once(time_interval);

#else

      //schedule timmod work on a interval time
      schedule_work(time_interval);
      pause();
#endif
   }
}


/******************************************************************************/
int main (int argc, char **argv)
{

	test_case = NORMAL_TEST;
//	test_case = DISCONTINUITY_TEST;
	//test_case = ODD_POINT_TEST;
   // Silence warnings...
   argc = argc;
   argv[0][0] = argv[0][0];

   printf("==============================================\n");
   printf("Starting Test\n");

   //open devices and get clock handle
   open_devs();

   //initial devices
   init_devs();

   do_test();

   close_devs();

   return TEST_RESULT_PASS;
}


